﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;
#endif

namespace Jinndev {

    public class DragHelper : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {

        [Tooltip("冗余用")]
        public string key;

        /// <summary>
        /// 开始拖拽
        /// </summary>
        /// <param name="helper"></param>
        public delegate void OnStartDragAction(DragHelper helper, PointerEventData eventData);
        public OnStartDragAction onStartDrag;

        /// <summary>
        /// 正在拖拽
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="deltaX">移动的屏幕像素距离x</param>
        /// <param name="deltaY">移动的屏幕像素距离y</param>
        public delegate void OnDragAction(DragHelper helper, PointerEventData eventData, float deltaX, float deltaY);
        public OnDragAction onDrag;

        /// <summary>
        /// 结束拖拽
        /// </summary>
        /// <param name="helper"></param>
        public delegate void OnEndDragAction(DragHelper helper, PointerEventData eventData);
        public OnEndDragAction onEndDrag;

        public bool useWorldPosition = false;
        public bool useUIRaycast = true;   // 是否使用UI接收输入
        public DragHelper asUIRaycastAgentFor = null;   // 当ui输入时，作为其他Draghelper的代理

        public bool dragging { get; private set; }
        public bool dragMoved { get; private set; }
        public Vector2 lastPointerPosition { get; private set; }
        public bool pointerDown { get; private set; }

        private List<Behaviour> componentsToRevert = new List<Behaviour>();

        //private void Awake() {
        //    onDrag = (x, y) => {
        //        transform.position -= new Vector3(x, y, 0) * 0.01f;
        //    };
        //    onEndDrag = () => {
        //        Debug.Log("End");
        //    };
        //}

        private void Awake() {
#if ENABLE_INPUT_SYSTEM
        if (Mouse.current != null) {
        }
        else if (Touchscreen.current != null && !EnhancedTouchSupport.enabled) {
            EnhancedTouchSupport.Enable();
        }
#endif
        }

        private void OnEnable() {

        }

        private void OnDisable() {
            dragging = false;
            pointerDown = false;
        }

        private void Update() {
            if (!useUIRaycast) {
                OnGlobalInput();
            }
        }

        private Vector3 GetPosition(PointerEventData eventData) {
            if (useWorldPosition) {
                return eventData.pointerCurrentRaycast.worldPosition;
            }
            else {
                return eventData.position;
            }
        }

        public void OnBeginDrag(PointerEventData eventData) {
            if (!useUIRaycast || IsMultipleTouch()) {
                return;
            }
            if (asUIRaycastAgentFor != null && asUIRaycastAgentFor.asUIRaycastAgentFor != this) {
                asUIRaycastAgentFor.OnBeginDrag(eventData);
                DisableOtherClickReceiver();
                return;
            }

            //dragging = true;
            StartDrag(eventData, GetPosition(eventData));
        }

        public void OnDrag(PointerEventData eventData) {
            if (!useUIRaycast || IsMultipleTouch()) {
                return;
            }
            if (asUIRaycastAgentFor != null && asUIRaycastAgentFor.asUIRaycastAgentFor != this) {
                asUIRaycastAgentFor.OnDrag(eventData);
                return;
            }

            //if (dragging == false) {
            //    StartDrag(eventData, GetPosition(eventData));
            //}
            //else {
            //    Dragging(eventData, GetPosition(eventData));
            //}
            Dragging(eventData, GetPosition(eventData));
        }

        public void OnEndDrag(PointerEventData eventData) {
            if (!useUIRaycast) {
                return;
            }
            if (asUIRaycastAgentFor != null && asUIRaycastAgentFor.asUIRaycastAgentFor != this) {
                asUIRaycastAgentFor.OnEndDrag(eventData);
                RevertOtherClickReceiver();
                return;
            }

            if (dragging) {
                EndDrag(eventData);
            }
        }

        private void DisableOtherClickReceiver() {
            Button button = GetComponent<Button>();
            if (button != null && button.enabled) {
                button.enabled = false;
                componentsToRevert.Add(button);
            }
        }

        private void RevertOtherClickReceiver() {
            foreach (Behaviour com in componentsToRevert) {
                com.enabled = true;
            }
            componentsToRevert.Clear();
        }

        private bool IsMultipleTouch() {
#if ENABLE_INPUT_SYSTEM
            return Touchscreen.current != null && Touch.activeFingers.Count > 1;
#else
            return Input.touchCount > 1;
#endif
        }

        private void OnGlobalInput() {
#if ENABLE_INPUT_SYSTEM
            if (Touchscreen.current != null) {
                var fingers = Touch.activeFingers;
                if (fingers.Count > 1) {
                    EndDrag(null);
                }
                else {
                    if (!pointerDown && fingers.Count == 1) {
                        pointerDown = true;
                        StartDrag(null, fingers[0].screenPosition);
                    }
                    else if (pointerDown && fingers.Count == 1) {
                        if (dragging == false) {
                            StartDrag(null, fingers[0].screenPosition);
                        }
                        else {
                            Dragging(null, fingers[0].screenPosition);
                        }
                    }
                    else {
                        pointerDown = false;
                        EndDrag(null);
                    }
                }
            }
#else
            if (Input.touchCount > 1) {
                EndDrag(null);
            }
            else {
                if (Input.GetMouseButtonDown(0)) {
                    pointerDown = true;
                    StartDrag(null, Input.mousePosition);
                }
                else if (Input.GetMouseButtonUp(0)) {
                    pointerDown = false;
                    EndDrag(null);
                }
                else if (pointerDown) {
                    Dragging(null, Input.mousePosition);
                }
            }
#endif
        }

        private void StartDrag(PointerEventData eventData, Vector2 position) {
            dragMoved = false;
            lastPointerPosition = position;
            if (onStartDrag != null) {
                onStartDrag.Invoke(this, eventData);
            }
        }

        private void Dragging(PointerEventData eventData, Vector2 position) {
            dragging = true;
            float deltaX = lastPointerPosition.x - position.x;
            float deltaY = lastPointerPosition.y - position.y;
            if (onDrag != null) {
                onDrag.Invoke(this, eventData, deltaX, deltaY);
            }
            lastPointerPosition = position;
            if (deltaX != 0 || deltaY != 0) {
                dragMoved = true;
            }
        }

        private void EndDrag(PointerEventData eventData) {
            if (onEndDrag != null) {
                onEndDrag.Invoke(this, eventData);
            }
            dragging = false;
        }

        public void AddOnStartDrag(OnStartDragAction onStartDrag) {
            this.onStartDrag += onStartDrag;
        }

        public void AddOnDrag(OnDragAction onDrag) {
            this.onDrag += onDrag;
        }

        public void AddOnEndDrag(OnEndDragAction onEndDrag) {
            this.onEndDrag += onEndDrag;
        }


    }

}
